﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace TestByWire
{
    public interface IScopeInformation
    {
        IContainerInteraction[] GetInteractions(Scope scope);
    }

    public class ScopeInformation : IScopeInformation
    {
        private readonly MethodInfo _methodInfo;
        private IContainerInteraction[] _localScope;
        private IContainerInteraction[] _globalScope;
        private IContainerInteraction[] _dynamicScope;

        public ScopeInformation(MethodInfo methodInfo, ScopeInformation oldInfo)
        {
            if (methodInfo == null)
            {
                throw new ArgumentNullException("methodInfo");
            }

            if (oldInfo != null)
            {
                Boolean globalIsReuseable = methodInfo.DeclaringType == oldInfo._methodInfo.DeclaringType;
                if (globalIsReuseable)
                    _globalScope = oldInfo._globalScope;

                Boolean localIsReuseable = methodInfo.Name == oldInfo._methodInfo.Name;
                if (globalIsReuseable && localIsReuseable)
                {
                    _localScope = oldInfo._localScope;
                    _dynamicScope = oldInfo._dynamicScope;
                }
            }
            _methodInfo = methodInfo;
        }

        public IContainerInteraction[] GetInteractions(Scope scope)
        {
            if (scope == Scope.Dynamic)
            {
                if (_dynamicScope == null)
                {
                    _dynamicScope = AssertRedundantInteractions(DynamicDeclarations.GetDynamicInteractions());
                }
                return _dynamicScope;
            }

            if (scope == Scope.Local)
            {
                if (_localScope == null)
                {
                    _localScope = AssertRedundantInteractions((IContainerInteraction[])_methodInfo.GetCustomAttributes(typeof(IContainerInteraction), true));
                }
                return _localScope;
            }

            if (scope == Scope.Global)
            {
                if (_globalScope == null)
                {
                    _globalScope = AssertRedundantInteractions((IContainerInteraction[])_methodInfo.DeclaringType.GetCustomAttributes(typeof(IContainerInteraction), true));
                }
                return _globalScope;
            }

            throw new NotImplementedException(string.Format("Scope {0} is not implemented", scope));
        }

        private static IContainerInteraction[] AssertRedundantInteractions(IContainerInteraction[] containerInteractions)
        {
            IList<IContainerInteraction> netList = new List<IContainerInteraction>();
            foreach (var interaction in containerInteractions)
            {
                if (netList.Contains(interaction, new InteractionEqualityComparer()))
                    throw new InvalidOperationException("Redundant stub declaration in same scope");

                netList.Add(interaction);
            }
            return netList.ToArray();
        }
    }

    public enum Scope
    {
        Local,
        Global,
        Dynamic
    }
}